package parser;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.OperationNotSupportedException;

import exceptions.IllFormattedLineException;

/**
 * Repräsentiert eine Zeile = Tag + Liste von Unterfeldern.
 * 
 * @author Christian
 *
 */
public class Line {

	public final Tag tag;

	public final boolean related;

	List<Subfield> subfields = new LinkedList<Subfield>();

	private static final Pattern ID_PAT = Pattern.compile("!(\\d+X?)!");

	public Line(final Tag tag, String content) // content nicht final!
			throws IllFormattedLineException {
		if (tag == null || content == null || content.trim().length() == 0)
			throw new IllegalArgumentException();

		this.tag = tag;
		// ƒ durch $ ersetzen (II.0)
		content = content.replaceAll("ƒ", "\\$");
		// content zerlegen:
		// II.1
		Set<Indicator> descSigns = tag.getDescriptionStrings();
		for (Indicator indicator : descSigns) {
			content =
				content.replaceAll(indicator.descriptionStr, "\\$"
					+ indicator.indicatorChar);
		}

		Matcher mID = ID_PAT.matcher(content);
		String contentRepl;
		if (mID.find()) {
			contentRepl = content.substring(0, mID.start());
			contentRepl += "$9";
			contentRepl += mID.group(1);
			contentRepl += "$8";
			contentRepl += content.substring(mID.end());
			
		} else {
			contentRepl = content;
			
		}
		
		if (contentRepl.contains("$9"))
			related = true;
		else
			related = false;
		
		// II.3
		if (contentRepl.charAt(0) != '$') {
			Indicator ohne = tag.getOhne();
			if (ohne == null)
				throw new IllFormattedLineException(
													"kein -ohne- gefunden für: "
														+ tag.pica3 + " "
														+ content);
			contentRepl = "$" + tag.getOhne().indicatorChar + contentRepl;

		}

		addSubfields(contentRepl);

	}

	/**
	 * Zerlegt in Unterfelder.
	 * @param content	Enthält garantiert mindestens $ und Indikator.
	 * @throws IllFormattedLineException
	 */
	private void addSubfields(final String content)
		throws IllFormattedLineException {
		int length = content.length();
		int allowedDollar = 0;
		Subfield sub;

		// wenn Relation, dürfen optionale nicht eingeschlossen werden:
		Indicator ind = tag.getIndicator(content.charAt(1), !related);
		int nextDollar = content.indexOf('$', allowedDollar + 2);

		while (nextDollar != -1 && nextDollar < length - 1) {
			char nextIndChar = content.charAt(nextDollar + 1);
			// wenn Relation, dürfen optionale nicht eingeschlossen werden:
			Indicator nextInd = tag.getIndicator(nextIndChar, !related);
			if (nextInd != null) {
				sub =
					new Subfield(ind, content.substring(allowedDollar + 2,
							nextDollar));
				add(sub);
				ind = nextInd;
				allowedDollar = nextDollar;
			}
			nextDollar = content.indexOf('$', nextDollar + 2);
		}
		sub = new Subfield(ind, content.substring(allowedDollar + 2));
		add(sub);

	}

	private void add(final Subfield subfield)
		throws IllFormattedLineException {
		if (isAllowed(subfield.indicator))
			subfields.add(subfield);
		else
			throw new IllFormattedLineException("Unterfeld doppelt:"
				+ subfield);
	}

	/**
	 * @return the subfields
	 */
	public List<Subfield> getSubfields() {
		return Collections.unmodifiableList(subfields);
	}

	@Override
	public final String toString() {
		String s = tag.pica3 + " ";
		
		for (Iterator<Subfield> iterator = subfields.iterator(); iterator.hasNext();) {
			Subfield subfield =  iterator.next();
			s += subfield;
//			if (iterator.hasNext())
//				s +=  " |";			
		}
		
		return s;
	}

	/**
	 * 
	 * Addiert den Inhalt von line zum Feld hinzu.
	 * 
	 * @param line nicht null
	 * @throws OperationNotSupportedException 
	 * 			Wenn der Inhalt von line nicht zum Feld 
	 * 			hinzugefügt werden darf.
	 */
	public final void add(final Line line)
		throws OperationNotSupportedException {
		if (line == null)
			throw new IllegalArgumentException();

		char repSign = tag.getRepetitionChar();
		if (repSign == 0)
			throw new OperationNotSupportedException();
		// zur Zeit gibt es dann nur ein Unterfeld
		Subfield otherSubf = this.subfields.get(0);
		Subfield mySubf = this.subfields.get(0);
		mySubf.content += repSign + otherSubf.content;
	}

	private boolean isAllowed(final Indicator ind) {
		if (ind.repeatability != Repeatability.NON_REPEATABLE)
			return true;
		// also nicht wiederholbar oder unbekannt:
		char indC = ind.indicatorChar;
		for (Subfield subfield : subfields) {
			if (indC == subfield.indicator.indicatorChar)
				return false;
		}
		return true;
	}

	/**
	 * @param args
	 * @throws IllFormattedLineException 
	 * @throws OperationNotSupportedException 
	 */
	public static void main(final String[] args)
		throws IllFormattedLineException, OperationNotSupportedException {
		Tag tag = TagDB.findTag("100");
		String content = "$PSchmidt$lFamilie, Oberstein, Idar-Oberstein";
		Line line = new Line(tag, content);
		Record record = new Record();
		record.add(line);
		System.out.println(record);
	}

}
